package com.ydj.framework.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.ydj.framework.exception.CommonException;
import com.ydj.framework.exception.RequestParameterException;
import com.ydj.framework.protocol.BaseEnum;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.ReflectionUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.Map;

@Configuration
public class JsonConfig {

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper mapper = builder.createXmlMapper(false).build();
        SimpleModule simpleModule = new SimpleModule();

        // 时间的转换，把Date类型，全部变形Long型，而且还是以秒的方式表示
        simpleModule.addSerializer(Date.class, new DateSerializer())
                .addDeserializer(Date.class, new DateDeSerializer());
        // 时间的转换，Boolean，变成0和1
        simpleModule.addSerializer(Boolean.class, new BooleanSerializer())
                .addDeserializer(Boolean.class, new BooleanDeSerializer());
        // Map的转换
        simpleModule.addSerializer(Map.class, new MapSerializer());


        mapper.registerModule(simpleModule);
        // 空对象时候，不会报错
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // 不要调用get和set的方法了，直接反射拿成员变量去注入
        mapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

        // 只要json内容是null的，一律不返回给前端（一律不序列化）
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return mapper;
    }

    public static class DateSerializer extends JsonSerializer<Date> {
        @Override
        public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeNumber(date.getTime());
        }
    }

    public static class DateDeSerializer extends JsonDeserializer<Date> {
        @Override
        public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            return new Date(jsonParser.getLongValue());
        }
    }

    public static class BooleanSerializer extends JsonSerializer<Boolean> {
        @Override
        public void serialize(Boolean value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeNumber(value ? 1 : 0);
        }
    }

    public static class BooleanDeSerializer extends JsonDeserializer<Boolean> {
        @SneakyThrows
        @Override
        public Boolean deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
            try {
                return jsonParser.getBooleanValue();
            } catch (Exception ignored) {

            }

            try {
                int value = jsonParser.getIntValue();
                if (value == 0) {
                    return false;
                } else if (value == 1) {
                    return true;
                } else {
                    String msg = "解析JSON错误. " + jsonParser.getCurrentName() + "是布尔值，解析不了0(true)和1(false)以外的，现在值是=" + value;
                    throw new RequestParameterException(msg);
                }
            } catch (Exception e) {
                String msg = "解析JSON错误. " + jsonParser.getCurrentName() + "字段解析有失败";
                throw new RequestParameterException(msg);
            }
        }
    }

    public static class MapSerializer extends JsonSerializer<Map> {
        @Override
        public void serialize(Map map, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeObject(map.values());
        }
    }

    public static class BaseEnumSerializer extends JsonSerializer<BaseEnum> {
        @Override
        public void serialize(BaseEnum value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeObject(value.getProtocolName());
        }
    }

    public static class BaseEnumDeSerializer extends JsonDeserializer<BaseEnum> {

        @SneakyThrows
        @Override
        public BaseEnum deserialize(JsonParser jp, DeserializationContext deserializationContext) {
            JsonNode node = jp.getCodec().readTree(jp);
            String currentName = jp.currentName();
            Object currentValue = jp.getCurrentValue();
            Class<?> findPropertyType = null;

            if (currentValue instanceof Collection) {
                // 如果是队列
                JsonStreamContext parsingContext = jp.getParsingContext();
                JsonStreamContext parent = parsingContext.getParent();
                Object currentValue3 = parent.getCurrentValue();
                String currentName3 = parent.getCurrentName();
                try {
                    Field listField = ReflectionUtils.findField(currentValue3.getClass(), currentName3);
                    ParameterizedType listGenericType = (ParameterizedType) listField.getGenericType();
                    Type listActualTypeArguments = listGenericType.getActualTypeArguments()[0];
                    findPropertyType = (Class<?>) listActualTypeArguments;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else {
                // 如果不是队列
                findPropertyType = BeanUtils.findPropertyType(currentName, currentValue.getClass());
            }
            if (findPropertyType == null) {
                throw new CommonException("内部错误，找不到枚举的类型");
            }
            if (node.getNodeType() != JsonNodeType.STRING) {
                throw new CommonException(currentName + "字段请输入字符串");
            }
            String text = node.asText();

            Method enumValuesMethod = findPropertyType.getMethod("values");
            BaseEnum[] enumValues = (BaseEnum[]) enumValuesMethod.invoke(null);
            for (BaseEnum enumValue : enumValues) {
                if (enumValue.getProtocolName().toLowerCase(Locale.ROOT).equals(text.toLowerCase(Locale.ROOT))) {
                    return enumValue;
                }
            }

            throw new CommonException(jp.getCurrentName() + "字段枚举值没办法匹配上，请联系客服");
        }
    }


}